﻿namespace NetFx.MSIL
{
  using System;
  using System.Text;
  using System.Collections;

  using System.Reflection;
  using System.Reflection.Emit;
  using System.Runtime.InteropServices;

  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Diagnostics.SymbolStore;
  using System.Security;
  using System.Security.Permissions;

  
  public class TypeGenerator:MemberGenerator,TypeContainer
  {
    #region Statics
    public static TypeGenerator of(TypeBuilder builder)
    {
      return new TypeGenerator(builder);
    }
    #endregion
    #region Fields
    protected ArrayList interfaces = new ArrayList();
    protected ArrayList fields = new ArrayList();
    protected ArrayList properties = new ArrayList();
    protected ArrayList methods = new ArrayList();
    protected ArrayList events = new ArrayList();
    protected ArrayList types = new ArrayList();
    protected ArrayList genericParameters = new ArrayList();
    #endregion
    #region Properties
    public override DefType GeneratorType
    {
      get
      {
        return DefType.Type;
      }
    }
    public virtual TypeBuilder Builder
    {
      get
      {
        return this.builder as TypeBuilder;
      }
    }
    public virtual Assembly Assembly
    {
      get
      {
        return this.Builder.Assembly;
      }
    }
    public virtual string AssemblyQualifiedName
    {
      get
      {
        return this.Builder.AssemblyQualifiedName;
      }
    }
    public virtual string FullName
    {
      get
      {
        return this.Builder.FullName;
      }
    }
    public virtual string Namespace
    {
      get
      {
        return this.Builder.Namespace;
      }
    }
    public virtual int Size
    {
      get
      {
        return this.Builder.Size;
      }
    }
    public virtual PackingSize PackageSize
    {
      get
      {
        return this.Builder.PackingSize;
      }
    }
    public virtual TypeAttributes Attributes
    {
      get
      {
        return this.Builder.Attributes;
      }
    }
    public virtual MethodBase DeclaringMethod
    {
      get
      {
        return this.Builder.DeclaringMethod;
      }
    }
    public virtual Type ParentType
    {
      get
      {
        return this.Builder.BaseType;
      }
      set
      {
        this.Builder.SetParent(value);
      }
    }
    public virtual TypeToken TypeToken
    {
      get
      {
        return this.Builder.TypeToken;
      }
    }
    public virtual Guid GUID
    {
      get
      {
        return this.Builder.GUID;
      }
    }
    public virtual IList InterfaceImplementations
    {
      get
      {
        return this.interfaces;
      }
    }
    public virtual IList Fields
    {
      get
      {
        return this.fields;
      }
    }
    public virtual IList Properties
    {
      get
      {
        return this.properties;
      }
    }
    public virtual IList Methods
    {
      get
      {
        return this.methods;
      }
    }
    public virtual IList Events
    {
      get
      {
        return this.events;
      }
    }
    public virtual IList Types
    {
      get
      {
        return this.types;
      }
    }
    public virtual IList GenericParameters
    {
      get
      {
        return this.genericParameters;
      }
    }
    #endregion
    #region Constructors
    protected TypeGenerator(MemberInfo member) : base(member) { }
    public TypeGenerator(TypeBuilder builder):base(builder)
    {
    }
    #endregion
    #region Methods
    public virtual void AddSecurity(SecurityAction action, PermissionSet pset)
    {
      this.Builder.AddDeclarativeSecurity(action, pset);
    }
    public virtual void AddInterfaceImplementation(Type interfaceType)
    {
      this.Builder.AddInterfaceImplementation(interfaceType);
    }
    public override void CommitGeneration()
    {
      if (!this.IsCommitted)
      {
        this.CommitTypes();
        this.Builder.CreateType();
        base.CommitGeneration();
      }
    }
    public virtual void CommitTypes()
    {
      if (this.Types.Count > 0)
      {
        foreach (TypeGenerator type in this.Types)
        {
          type.CommitGeneration();
        }
      }
    }
    #region Defines
    public virtual FieldGenerator DefineField(string fieldName, Type type, FieldAttributes attributes)
    {
      return this.AddField(FieldGenerator.of(this.WrapDefineField(fieldName, type, attributes)));
    }
    public virtual FieldGenerator DefineInitializedStaticField(string name, byte[] data, FieldAttributes attributes)
    {
      return this.AddField(FieldGenerator.of(this.WrapDefineInitializedStaticField(name, data, attributes)));
    }
    public virtual FieldGenerator DefineUninitializedStaticField(string name, int size, FieldAttributes attributes)
    {
      return this.AddField(FieldGenerator.of(this.WrapDefineUninitializedStaticField(name, size, attributes)));
    }

    public virtual MethodGenerator DefineMethod(string name, MethodAttributes attributes)
    {
      return this.AddMethod(
          this.WrapDefineMethod(name,attributes)
        );
    }
    public virtual MethodGenerator DefineMethod(string name, MethodAttributes attributes, CallingConventions convertions)
    {
      return this.AddMethod(
          this.WrapDefineMethod(name,attributes,convertions)
        );
    }
    public virtual MethodGenerator DefineMethod(string name, MethodAttributes attributes, Type returnType, params Type[] parameterTypes)
    {
      return this.AddMethod(
          this.WrapDefineMethod(name,attributes,returnType,parameterTypes)
        );
    }

    public virtual MethodGenerator DefineMethod(string name, MethodAttributes attributes, CallingConventions convertions, Type returnType, params Type[] parameterTypes)
    {
      return this.AddMethod(
          this.WrapDefineMethod(name,attributes,convertions,returnType,parameterTypes)
        );
    }
    public virtual MethodGenerator DefineOverride(MethodBuilder methodInfoBody, MethodInfo methodInfoDeclaration)
    {
      return this.AddMethod(
          this.WrapDefineOverride(methodInfoBody,methodInfoDeclaration)
        );
    }
    public virtual MethodGenerator DefineConstructor(MethodAttributes attributes, CallingConventions convertions, params Type[] parameterType)
    {
      return this.AddMethod(
          this.WrapDefineConstructor(attributes,convertions,parameterType)
        );
    }
    public virtual MethodGenerator DefineDefaultConstructor(MethodAttributes attributes)
    {
      return this.AddMethod(
          this.WrapDefineDefaultConstructor(attributes)
        );
    }
    public virtual MethodGenerator DefineInitializer()
    {
      return this.AddMethod(
          this.WrapDefineInitializer()
        );
    }

    public virtual MethodGenerator DefinePInvokeMethod(string name, string dllName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
    {
      return this.AddMethod(
          this.WrapDefinePInvokeMethod(name,dllName,attributes,callingConvention,returnType,parameterTypes,nativeCallConv,nativeCharSet)
        );
    }
    public virtual MethodGenerator DefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
    {
      return this.AddMethod(
          this.WrapDefinePInvokeMethod(name,dllName,entryName,attributes,callingConvention,returnType,parameterTypes,nativeCallConv,nativeCharSet)
        );
    }
    public virtual PropertyGenerator DefineProperty(string name, PropertyAttributes attributes, Type returnType, Type[] parameterTypes)
    {
      return this.AddProperty(
          this.WrapDefineProperty(name,attributes,returnType,parameterTypes)
        );
    }

    public virtual EventGenerator DefineEvent(string name, EventAttributes attributes, Type eventtype)
    {
      return this.AddEvent(
          name,
          attributes,
          eventtype,
          this.WrapDefineEvent(name,attributes,eventtype)
        );
    }

    public virtual TypeGenerator DefineType(string name)
    {
      return this.AddType(
          this.WrapDefineType(name)
        );
    }
    public virtual TypeGenerator DefineType(string name, TypeAttributes attr)
    {
      return this.AddType(
          this.WrapDefineType(name,attr)
        );
    }
    public virtual TypeGenerator DefineType(string name, TypeAttributes attr, Type parent)
    {
      return this.AddType(
          this.WrapDefineType(name,attr,parent)
        );
    }
    public virtual TypeGenerator DefineType(string name, TypeAttributes attr, Type parent, PackingSize packSize)
    {
      return this.AddType(
          this.WrapDefineType(name,attr,parent,packSize)
        );
    }
    public virtual TypeGenerator DefineType(string name, TypeAttributes attr, Type parent, Type[] interfaces)
    {
      return this.AddType(
          this.WrapDefineType(name,attr,parent,interfaces)
        );
    }
    public virtual EnumGenerator DefineEnumeration(string name, TypeAttributes attributes, Type underlingType)
    {
      return this.AddType(InnerEnumGenerator.of(this.Builder,name,attributes,underlingType)) as EnumGenerator;
    }


    public virtual IList DefineGenericParameters(params string[] names)
    {
      GenericTypeParameterBuilder[] builders = this.WrapDefineGenericParameters(names);
      if (builders != null)
      {
        this.GenericParameters.Clear();
        foreach (GenericTypeParameterBuilder builder in builders)
        {
          this.GenericParameters.Add(GenericParameterGenerator.of(builder));
        }
      }
      return this.GenericParameters;
    }
    #endregion
    #region DefineGeneric
    protected virtual GenericTypeParameterBuilder[] WrapDefineGenericParameters(params string[] names)
    {
      return this.Builder.DefineGenericParameters(names);
    }
    #endregion
    #region Define Fields
    protected virtual FieldBuilder WrapDefineField(string fieldName, Type type,FieldAttributes attributes)
    {
      return this.Builder.DefineField(fieldName, type, attributes);
    }
    protected virtual FieldBuilder WrapDefineInitializedStaticField(string name, byte[] data, FieldAttributes attributes)
    {
      return this.Builder.DefineInitializedData(name, data, attributes);
    }
    protected virtual FieldBuilder WrapDefineUninitializedStaticField(string name,int size,FieldAttributes attributes)
    {
      return this.Builder.DefineUninitializedData(name, size, attributes);
    }
    #endregion
    #region Define Methods
    protected virtual MethodBuilder WrapDefineOverride(MethodBuilder methodInfoBody, MethodInfo methodInfoDeclaration)
    {
      this.Builder.DefineMethodOverride(methodInfoBody, methodInfoDeclaration);
      return methodInfoBody;
    }
    protected virtual MethodBuilder WrapDefineMethod(string name, MethodAttributes attributes)
    {
      return this.Builder.DefineMethod(name, attributes);
    }
    protected virtual MethodBuilder WrapDefineMethod(string name, MethodAttributes attributes, CallingConventions convertions)
    {
      return this.Builder.DefineMethod(name, attributes, convertions);
    }
    protected virtual MethodBuilder WrapDefineMethod(string name, MethodAttributes attributes, Type returnType, params Type[] parameterTypes)
    {
      return this.Builder.DefineMethod(name, attributes, returnType,parameterTypes);
    }
    protected virtual MethodBuilder WrapDefineMethod(string name, MethodAttributes attributes, CallingConventions convertions, Type returnType, params Type[] parameterTypes)
    {
      return this.Builder.DefineMethod(name, attributes, convertions, returnType, parameterTypes);
    }
    #endregion
    #region Define Constructors
    protected virtual ConstructorBuilder WrapDefineConstructor(MethodAttributes attributes, CallingConventions convertions, params Type[] parameterType)
    {
      return this.Builder.DefineConstructor(attributes, convertions, parameterType);
    }
    protected virtual ConstructorBuilder WrapDefineDefaultConstructor(MethodAttributes attributes)
    {
      return this.Builder.DefineDefaultConstructor(attributes);
    }
    protected virtual ConstructorBuilder WrapDefineInitializer()
    {
      return this.Builder.DefineTypeInitializer();
    }
    #endregion
    #region Define PInvokes
    protected virtual MethodBuilder WrapDefinePInvokeMethod(string name, string dllName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
    {
      return this.Builder.DefinePInvokeMethod(
        name,
        dllName,
        attributes,
        callingConvention,
        returnType,
        parameterTypes,
        nativeCallConv,
        nativeCharSet
        );
    }
    protected virtual MethodBuilder WrapDefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
    {
      return this.Builder.DefinePInvokeMethod(
        name,
        dllName,
        entryName,
        attributes,
        callingConvention,
        returnType,
        parameterTypes,
        nativeCallConv,
        nativeCharSet
        );
    }
    #endregion
    #region Define Properties
    protected virtual PropertyBuilder WrapDefineProperty(string name, PropertyAttributes attributes, Type returnType, Type[] parameterTypes)
    {
      return this.Builder.DefineProperty(name, attributes, returnType, parameterTypes);
    }
    #endregion
    #region Define Events
    protected virtual EventBuilder WrapDefineEvent(string name, EventAttributes attributes, Type eventtype)
    {
      return this.Builder.DefineEvent(name, attributes, eventtype);
    }
    #endregion
    #region Define InnerTypes
    protected virtual TypeBuilder WrapDefineType(string name)
    {
      return this.Builder.DefineNestedType(name);
    }
    protected virtual TypeBuilder WrapDefineType(string name, TypeAttributes attr)
    {
      return this.Builder.DefineNestedType(name, attr);
    }
    protected virtual TypeBuilder WrapDefineType(string name, TypeAttributes attr, Type parent)
    {
      return this.Builder.DefineNestedType(name, attr, parent);
    }
    protected virtual TypeBuilder WrapDefineType(string name, TypeAttributes attr, Type parent, PackingSize packSize)
    {
      return this.Builder.DefineNestedType(name, attr, parent, packSize);
    }
    protected virtual TypeBuilder WrapDefineType(string name, TypeAttributes attr, Type parent, Type[] interfaces)
    {
      return this.Builder.DefineNestedType(name, attr, parent, interfaces);
    }
    #endregion
    #region Utils
    protected virtual TypeGenerator AddType(TypeBuilder builder)
    {
      return this.AddType(TypeGenerator.of(builder));
    }
    protected virtual MethodGenerator AddMethod(MethodBuilder builder)
    {
      return this.AddMethod(MethodGenerator.of(builder));
    }
    protected virtual MethodGenerator AddMethod(ConstructorBuilder builder)
    {
      return this.AddMethod(MethodGenerator.of(builder));
    }
    protected virtual FieldGenerator AddField(FieldBuilder builder)
    {
      return this.AddField(FieldGenerator.of(builder));
    }
    protected virtual EventGenerator AddEvent(string name,EventAttributes attributes,Type eventType,EventBuilder builder)
    {
      return this.AddEvent(EventGenerator.of(name,attributes, eventType, builder));
    }
    protected virtual PropertyGenerator AddProperty(PropertyBuilder builder)
    {
      return this.AddProperty(PropertyGenerator.of(builder));
    }

    protected virtual TypeGenerator AddType(TypeGenerator g)
    {
      this.Types.Add(g);
      return g;
    }
    protected virtual FieldGenerator AddField(FieldGenerator g)
    {
      this.fields.Add(g);
      return g;
    }
    protected virtual EventGenerator AddEvent(EventGenerator g)
    {
      this.events.Add(g);
      return g;
    }
    protected virtual MethodGenerator AddMethod(MethodGenerator g)
    {
      this.methods.Add(g);
      return g;
    }
    protected virtual PropertyGenerator AddProperty(PropertyGenerator g)
    {
      this.properties.Add(g);
      return g;
    }
    #endregion
    #endregion
    #region Utils
    protected override void DisposeCollection(bool disposing)
    {
      if (disposing)
      {
        this.DisposeCollection(this.interfaces);
        this.DisposeCollection(this.fields);
        this.DisposeCollection(this.properties);
        this.DisposeCollection(this.methods);
        this.DisposeCollection(this.events);
        this.DisposeCollection(this.types);
      }
      base.DisposeCollection(disposing);
    }
    #endregion
  }
}
